﻿#include <windows.h>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <thread>
#include <assert.h>

#include <OpenXLSX/OpenXLSX.hpp>

using namespace std;
using namespace OpenXLSX;

std::string OpenInputFile();
std::string SelectOutputDir();
void SaveAsFiles(const std::string fileName, XLRowRange& headRows, XLRow& dataRow);
void OutputRow(std::vector<XLCellValue> values);
void GB2312ToUtf8(const std::string gbStr, std::string& utf8Str);
void Utf8ToGB2312(const std::string utf8Str, std::string& gbStr);

int g_startedThreadCnt = 0;
int g_endThreadCnt = 0;

int main(int argc, char** argv)
{
    std::string a = "输出";
    GB2312ToUtf8(a, a);
    // 1. 选择输入文件
    auto xlsxFile = OpenInputFile();

    // 2. 选择输出目录
    auto outputdir = SelectOutputDir();

    // 3. 加载文件并输出
    XLDocument doc;
    doc.open(xlsxFile);

    auto wb = doc.workbook();
    cout << "存在的工作表：" << endl;
    int i = 0;
    for (auto s : wb.worksheetNames())
    {
        Utf8ToGB2312(s, s);
        cout << "\t" << i++ << ": " << s <<endl;
    }
    cout << "选择要提取的工作表序号(默认0)：";
    int idx=0;
    cin >> idx;
    cout << "输入表头行数（默认2）：";
    int headcnt = 2;
    cin >> headcnt;
    cout << "输入目标文件名所在列（默认1）：";
    int nameCol = 1;
    cin >> nameCol;

    auto wks = doc.workbook().worksheet(wb.worksheetNames()[idx]);

    XLRowRange headerRows(wks.rows(1, headcnt));
    XLRowRange DataRows(wks.rows(headcnt+1, wks.rowCount()));

    int nullcnt = 0;
    for(auto row: DataRows)
    {
        std::vector<XLCellValue> values = row.values();
        OutputRow(values);
        if (values.empty()) continue;
        if (nameCol > values.size()) {
            cout << "Error!输入列数太大"<< nameCol << " > " << values.size() << endl;
            cout << "将 不输出本行 命名" << endl;
            system("pause");
            continue;
        }

        if(values[nameCol - 1].type() == XLValueType::Empty)
        {
            cout << "当前选择的命名列为空 ,将以 \"null" << nullcnt << "\" 命名" << endl;
            values[nameCol - 1] = "null" + std::to_string(nullcnt++);
            system("pause");
        }
        ostringstream oss;
        oss << values[nameCol - 1];
        std::string outputName = oss.str();


        std::string outputXlsx = outputdir +"/"+ outputName + ".xlsx";// utf8

        std::thread th(SaveAsFiles, outputXlsx, headerRows, row);
        th.detach();
        g_startedThreadCnt++;
        //SaveAsFiles(outputXlsx, headerRows, row);
    }

    while (g_endThreadCnt != g_startedThreadCnt)
    {
        this_thread::sleep_for(1s);
    }
    doc.close();
    // 4. prompt
    MessageBox(NULL, "提取成功！", "结果", MB_OK);

    return 0;
}

std::string OpenInputFile()
{
    char szFile[MAX_PATH] = { 0 };

    OPENFILENAME ofn;
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = MAX_PATH;
    //ofn.nFilterIndex = 1;
    ofn.lpstrFilter = "xlsx(*.xlsx)\0*.xlsx\0All File(*.*)\0*.*\0\0";// 格式：显示内容\0过滤器\0
    ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;
    ofn.lpstrTitle = "选择xlsx文件";
    auto ret = GetOpenFileName(&ofn);
    if (!ret)
    {
        cout << "Please open xlsx files!" << endl;
        assert(0);
    }
    cout << ofn.lpstrFile << endl;

    string xlsxFile = ofn.lpstrFile;
    GB2312ToUtf8(xlsxFile, xlsxFile);
    return xlsxFile;
}

std::string SelectOutputDir()
{
    char szFile[MAX_PATH] = "_";

    OPENFILENAME ofn;
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.lpstrFile = szFile;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_PATHMUSTEXIST| OFN_EXPLORER;
    ofn.lpstrTitle = "选择输出目录";
    auto ret = GetOpenFileName(&ofn);
    if(!ret)
    {
        cout << "Please select output directory!" << endl;
        return "";
    }
    std::string retStr(ofn.lpstrFile,ofn.nFileOffset -1);
    cout << "输出目录："<< retStr << endl;

    GB2312ToUtf8(retStr, retStr);
    return retStr;
}

void SaveAsFiles(const std::string fileName, XLRowRange& headRows, XLRow& dataRow)
{
    XLDocument doc;
    doc.create(fileName);
    auto wks = doc.workbook().worksheet("Sheet1");
    int i = 1;
    for (auto row : headRows)
    {
        wks.row(i++).values() = row.values();
    }

    wks.row(i++).values() = dataRow.values();
    
    doc.save();
    doc.close();
    g_endThreadCnt++;
}

void OutputRow(std::vector<XLCellValue> values)
{
    for (auto v : values)
    {
        if (v.type() == XLValueType::String) {
            std::string vv = v.get<std::string>();
            Utf8ToGB2312(vv, vv);
            cout << vv;
        }
        else if (v.type() == XLValueType::Empty) {
            cout << "\t";
        }
        else
            cout << v << " ";

    }
    cout << endl;
}

void GB2312ToUtf8(const std::string gbStr, std::string &utf8Str)
{
    // 2312 => utf16
    int wcLen = MultiByteToWideChar(CP_ACP, 0, gbStr.c_str(), -1, NULL, 0);
    WCHAR *wcbuf = new WCHAR[wcLen];
    MultiByteToWideChar(CP_ACP, 0, gbStr.c_str(), -1, wcbuf, wcLen);

    // utf16 => utf8
    int utfLen = WideCharToMultiByte(CP_UTF8, 0, wcbuf, -1, NULL, 0, NULL, NULL);
    utf8Str.resize(utfLen);
    WideCharToMultiByte(CP_UTF8, 0, wcbuf, -1, utf8Str.data(), utf8Str.length(), NULL, NULL);
    utf8Str.pop_back();// utf8str 中会多存储一个 \0
}

void Utf8ToGB2312(const std::string utf8Str, std::string& gbStr)
{
    // utf8 => utf16
    int wcLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
    WCHAR* wcbuf = new WCHAR[wcLen];
    MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wcbuf, wcLen);

    // utf16 => 2312
    int _2312Len = WideCharToMultiByte(CP_ACP, 0, wcbuf, -1, NULL, 0, NULL, NULL);
    gbStr.resize(_2312Len);
    WideCharToMultiByte(CP_ACP, 0, wcbuf, -1, gbStr.data(), gbStr.length(), NULL, NULL);
    gbStr.pop_back();// utf8str 中会多存储一个 \0
}

